﻿using System;
using System.Text;
using System.Text.RegularExpressions;
using FFmpegVideoClip.Configuration;
using FFmpegVideoClip.Lib;
using FFmpegVideoClip.Lib.AdvancedFragmentProcessor;


namespace FFmpegVideoClip.Mode.Files
{
    public class SuccessAndProgress : IDisposable
    {
        private AdvancedParametersSchemeModule module;
        public string SchemeName => module.SchemeName;
        public bool EnableWriteLog = false;
        private Regex? SuccessValidatedRegex;
        private Regex? GetProgressValueExpression;
        private ScriptInvoke? script;
        private bool m_IsSuccess = false;
        public bool IsSuccess { get => m_IsSuccess; set { if (m_IsSuccess == value) return; m_IsSuccess = value; if (m_IsSuccess) CompletedTimingSeconds = TotalSeconds; } }
        private string errorInfo = string.Empty;
        public string? ErrorInfo
        {
            get => errorInfo; set
            {
                if (ErrorInfo == value || string.IsNullOrEmpty(value))
                    return;
                errorInfo += $"{value.TrimEnd('\r', '\n')}\r\n";
                WriteNewInfo(errorInfo);
            }
        }

        public event Action<double>? ProgressChangeEvent;
        private double m_CompletedTimingSeconds = 0;
        public double CompletedTimingSeconds
        {
            get => m_CompletedTimingSeconds;
            set
            {
                if (m_CompletedTimingSeconds == value) return;
                m_CompletedTimingSeconds = value;
                ProgressChangeEvent?.Invoke(m_CompletedTimingSeconds);
            }
        }
        public double TotalSeconds { get; private set; } = 0;
        private StringBuilder m_log;
        public string Log => m_log.ToString();

        private Fragment? fragment { get; set; } = null;

        public SuccessAndProgress(AdvancedParametersSchemeModule module) { this.module = module; m_log = new StringBuilder(); }
        public SuccessAndProgress(AdvancedParametersSchemeModule module, Fragment fragment, bool IsAfterprocess = false) : this(module)
        { this.fragment = fragment; TotalSeconds = fragment.FragmentDuration.TotalSeconds; ProgressStatistics.AddElement(fragment.clip!.Name, fragment.Name, fragment.ID, fragment.FragmentDuration, this, IsAfterprocess); }
        public SuccessAndProgress(AdvancedParametersSchemeModule module, string FilePath) : this(module)
        { TotalSeconds = GetMediaInfo.GetInfo(FilePath).Duration.TotalSeconds; ProgressStatistics.AddElement(FilePath, TimeSpan.FromSeconds(TotalSeconds), this); }
        public SuccessAndProgress(AdvancedParametersSchemeModule module, string ClipName, double TotalSeconds) : this(module)
        { this.TotalSeconds = TotalSeconds; ProgressStatistics.AddElement(ClipName, TimeSpan.FromSeconds(TotalSeconds), this); }

        public void Init()
        {
            if (fragment != null)
            {
                TotalSeconds = fragment.FragmentDuration.TotalSeconds;
                ProgressStatistics.UpdateElement(fragment.ID, fragment.FragmentDuration, this);
            }
            CallProcessor callProcessor = module.GetCallProcessor();

            if (!string.IsNullOrEmpty(callProcessor.SuccessValidatedExpression))
                SuccessValidatedRegex = new Regex(callProcessor.SuccessValidatedExpression, RegexOptions.IgnoreCase);

            if (!string.IsNullOrEmpty(callProcessor.GetProgressValueExpression))
                GetProgressValueExpression = new Regex(callProcessor.GetProgressValueExpression, RegexOptions.IgnoreCase);

            if (!string.IsNullOrEmpty(callProcessor.ProgressScript))
                script = ScriptManage.CallProgressScriptDic?[callProcessor.ID];
        }
        public void ReleaseScript()
        {
            ProgressChangeEvent = null;
            if (script == null)
                return;
            script = null;
        }
        public void SuccessValidated(string? value)
        {
            if (string.IsNullOrEmpty(value))
                return;
            WriteLineNewInfo(value);
            try
            {
                if (SuccessValidatedRegex is null)
                    IsSuccess = true;
                else if (SuccessValidatedRegex.IsMatch(value))
                    IsSuccess = true;

                if (script is not null && GetProgressValueExpression is not null)
                {
                    Match m = GetProgressValueExpression.Match(value);
                    if (string.IsNullOrEmpty(m.Value))
                        return;

                    double ScriptResult = script.Invoke(TotalSeconds, m.Value);
                    
                    if(ScriptResult > 0)
                        CompletedTimingSeconds = ScriptResult;
                }
            }
            catch (Exception exc)
            {
                ErrorInfo = $"处理器[{module.SchemeName}]异常:{exc.Message}\r\n{exc.StackTrace}";
            }

        }

        public void LogClear() => m_log.Clear();
        public void WriteNewInfo(string text) { if (Config.IsEnableWriteLog) m_log.Append(text); }
        public void WriteLineNewInfo(string text) { if (Config.IsEnableWriteLog || EnableWriteLog) m_log.AppendLine(text); }
        public void WriteExceptionInfo(string OwnerInfo, Exception exception)
        {
            if (!Config.IsEnableWriteLog) return;
            m_log.AppendLine(OwnerInfo);
            m_log.AppendLine(exception.Message);
            m_log.AppendLine(exception.StackTrace);
        }

        #region Disposed
        private bool disposedValue;
        protected virtual void OnDispose()
        {
            if (!disposedValue)
            {
                ReleaseScript();
                module = null!;
                SuccessValidatedRegex = null!;
                GetProgressValueExpression = null!;
                errorInfo = null!;
                m_log.Clear();
                m_log = null!;
                fragment = null!;
                disposedValue = true;
            }
        }
        ~SuccessAndProgress() { OnDispose(); }
        public void Dispose() { OnDispose(); GC.SuppressFinalize(this); }
        #endregion

    }
}
